{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "dc6a3d0b",
   "metadata": {},
   "source": [
    "# 分类变量：\n",
    "##### 分类变量类似于枚举，拥有特定数量的值类型\n",
    "比如一项调查，询问你多久吃一次早餐，并提供四个选项:“从不”、“很少”、“大多数日子”或“每天”。在本例中，数据是分类的，因为答案属于一组固定的类别。如果对人们所拥有的汽车品牌进行调查，回答可以分为“本田”、“丰田”和“福特”。在本例中，数据也是分类的。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c0c66246",
   "metadata": {},
   "source": [
    "### 预处理分类变量的方法：\n",
    "##### 1.删除分类变量\n",
    "处理分类变量最简单的方法是从数据集中删除它们。这种方法适用于该列中不包含有用信息的情况\n",
    "##### 2.标签编码\n",
    "标签编码将每个变量类型标记为不同的整数，对于类别有个唯一的排名，并不是所有的分类变量在值中都有一个明确的顺序，但是我们将那些有顺序的变量称为有序变量\n",
    "##### 3.One-Hot编码\n",
    "“One-hot”编码创建新列，表明原始数据中每个可能值的存在(或不存在)，与标签编码不同，one-hot编码不假定类别的顺序。因此，如果在分类数据中没有明确的顺序，我们把没有内在排序的分类变量称为名义变量\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "73ca2806",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\ProgramData\\Anaconda3\\lib\\site-packages\\pandas\\core\\frame.py:4901: SettingWithCopyWarning: \n",
      "A value is trying to be set on a copy of a slice from a DataFrame\n",
      "\n",
      "See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy\n",
      "  return super().drop(\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Type</th>\n",
       "      <th>Method</th>\n",
       "      <th>Regionname</th>\n",
       "      <th>Rooms</th>\n",
       "      <th>Distance</th>\n",
       "      <th>Postcode</th>\n",
       "      <th>Bedroom2</th>\n",
       "      <th>Bathroom</th>\n",
       "      <th>Landsize</th>\n",
       "      <th>Lattitude</th>\n",
       "      <th>Longtitude</th>\n",
       "      <th>Propertycount</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>12167</th>\n",
       "      <td>u</td>\n",
       "      <td>S</td>\n",
       "      <td>Southern Metropolitan</td>\n",
       "      <td>1</td>\n",
       "      <td>5.0</td>\n",
       "      <td>3182.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.0</td>\n",
       "      <td>-37.85984</td>\n",
       "      <td>144.9867</td>\n",
       "      <td>13240.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6524</th>\n",
       "      <td>h</td>\n",
       "      <td>SA</td>\n",
       "      <td>Western Metropolitan</td>\n",
       "      <td>2</td>\n",
       "      <td>8.0</td>\n",
       "      <td>3016.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>2.0</td>\n",
       "      <td>193.0</td>\n",
       "      <td>-37.85800</td>\n",
       "      <td>144.9005</td>\n",
       "      <td>6380.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8413</th>\n",
       "      <td>h</td>\n",
       "      <td>S</td>\n",
       "      <td>Western Metropolitan</td>\n",
       "      <td>3</td>\n",
       "      <td>12.6</td>\n",
       "      <td>3020.0</td>\n",
       "      <td>3.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>555.0</td>\n",
       "      <td>-37.79880</td>\n",
       "      <td>144.8220</td>\n",
       "      <td>3755.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2919</th>\n",
       "      <td>u</td>\n",
       "      <td>SP</td>\n",
       "      <td>Northern Metropolitan</td>\n",
       "      <td>3</td>\n",
       "      <td>13.0</td>\n",
       "      <td>3046.0</td>\n",
       "      <td>3.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>265.0</td>\n",
       "      <td>-37.70830</td>\n",
       "      <td>144.9158</td>\n",
       "      <td>8870.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6043</th>\n",
       "      <td>h</td>\n",
       "      <td>S</td>\n",
       "      <td>Western Metropolitan</td>\n",
       "      <td>3</td>\n",
       "      <td>13.3</td>\n",
       "      <td>3020.0</td>\n",
       "      <td>3.0</td>\n",
       "      <td>1.0</td>\n",
       "      <td>673.0</td>\n",
       "      <td>-37.76230</td>\n",
       "      <td>144.8272</td>\n",
       "      <td>4217.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "      Type Method             Regionname  Rooms  Distance  Postcode  Bedroom2  \\\n",
       "12167    u      S  Southern Metropolitan      1       5.0    3182.0       1.0   \n",
       "6524     h     SA   Western Metropolitan      2       8.0    3016.0       2.0   \n",
       "8413     h      S   Western Metropolitan      3      12.6    3020.0       3.0   \n",
       "2919     u     SP  Northern Metropolitan      3      13.0    3046.0       3.0   \n",
       "6043     h      S   Western Metropolitan      3      13.3    3020.0       3.0   \n",
       "\n",
       "       Bathroom  Landsize  Lattitude  Longtitude  Propertycount  \n",
       "12167       1.0       0.0  -37.85984    144.9867        13240.0  \n",
       "6524        2.0     193.0  -37.85800    144.9005         6380.0  \n",
       "8413        1.0     555.0  -37.79880    144.8220         3755.0  \n",
       "2919        1.0     265.0  -37.70830    144.9158         8870.0  \n",
       "6043        1.0     673.0  -37.76230    144.8272         4217.0  "
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import pandas as pd \n",
    "from sklearn.model_selection import train_test_split\n",
    "\n",
    "# 自己定义的方法，用于比较处理缺失值的不同方法,内部用的是随机森林模型\n",
    "from score_dataset import score_dataset\n",
    "\n",
    "# Read the data \n",
    "data = pd.read_csv('../melb_data.csv')\n",
    "\n",
    "# Separate target from predictors 将目标与预测分开\n",
    "y = data.Price\n",
    "X = data.drop(['Price'], axis=1)\n",
    "\n",
    "# Divide data into training and validation subsets\n",
    "X_train_full, X_valid_full, y_train, y_valid = train_test_split(X, y, train_size=0.8, test_size=0.2,random_state=0)\n",
    "\n",
    "# Drop columns with missing values (simplest approach)\n",
    "cols_with_missing = [col for col in X_train_full.columns if X_train_full[col].isnull().any()]\n",
    "X_train_full.drop(cols_with_missing, axis=1, inplace=True)\n",
    "X_valid_full.drop(cols_with_missing, axis=1, inplace=True)\n",
    "\n",
    "# \"Cardinality\" means the number of unique values in a column\n",
    "# Select categorical columns with relatively low cardinality (convenient but arbitrary)\n",
    "# “基数”是指在一列中唯一值的数量,选择基数相对较低的分类列（方便但任意）\n",
    "low_cardinality_cols = [cname for cname in X_train_full.columns if X_train_full[cname].nunique() < 10 and \n",
    "                        X_train_full[cname].dtype == \"object\"]\n",
    "\n",
    "# Select numerical columns\n",
    "numerical_cols = [cname for cname in X_train_full.columns if X_train_full[cname].dtype in ['int64', 'float64']]\n",
    "\n",
    "# Keep selected columns only\n",
    "my_cols = low_cardinality_cols + numerical_cols\n",
    "X_train = X_train_full.loc[:,my_cols].copy()\n",
    "X_valid = X_valid_full.loc[:,my_cols].copy()\n",
    "# 用head()方法瞄一眼X_train\n",
    "X_train.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "aad12f21",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Categorical variables:\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "['Type', 'Method', 'Regionname']"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 获取分类列表\n",
    "s = (X_train.dtypes == 'object')\n",
    "object_cols = list(s[s].index)\n",
    "\n",
    "print(\"Categorical variables:\")\n",
    "object_cols"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "22c587f4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MAE from Approach 1 (Drop categorical variables):\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "174632.25689207987"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "'''\n",
    "1.删除分类变量\n",
    "   处理分类变量最简单的方法是从数据集中删除它们。这种方法适用于该列中不包含有用信息的情况\n",
    "'''\n",
    "drop_X_train = X_train.select_dtypes(exclude=['object'])\n",
    "drop_X_valid = X_valid.select_dtypes(exclude=['object'])\n",
    "\n",
    "print(\"MAE from Approach 1 (Drop categorical variables):\")\n",
    "score_dataset(drop_X_train, drop_X_valid, y_train, y_valid)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "0bf1b18f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MAE from Approach 2 (Label Encoding):\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "165256.28786135072"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "'''\n",
    "2.标签编码\n",
    "   标签编码将每个变量类型标记为不同的整数\n",
    "'''\n",
    "from sklearn.preprocessing import LabelEncoder\n",
    "\n",
    "# 复制一份数据防止改变源数据\n",
    "label_X_train = X_train.copy()\n",
    "label_X_valid = X_valid.copy()\n",
    "\n",
    "# 将标签编码器分别应用于每一列\n",
    "label_encoder = LabelEncoder()\n",
    "for col in object_cols:\n",
    "    label_X_train[col] = label_encoder.fit_transform(X_train[col])\n",
    "    label_X_valid[col] = label_encoder.transform(X_valid[col])\n",
    "\n",
    "print(\"MAE from Approach 2 (Label Encoding):\")\n",
    "score_dataset(label_X_train, label_X_valid, y_train, y_valid)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "581858fa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "       Rooms  Distance  Postcode  Bedroom2  Bathroom  Landsize  Lattitude  \\\n",
      "12167      1       5.0    3182.0       1.0       1.0       0.0  -37.85984   \n",
      "6524       2       8.0    3016.0       2.0       2.0     193.0  -37.85800   \n",
      "8413       3      12.6    3020.0       3.0       1.0     555.0  -37.79880   \n",
      "2919       3      13.0    3046.0       3.0       1.0     265.0  -37.70830   \n",
      "6043       3      13.3    3020.0       3.0       1.0     673.0  -37.76230   \n",
      "...      ...       ...       ...       ...       ...       ...        ...   \n",
      "13123      3       5.2    3056.0       3.0       1.0     212.0  -37.77695   \n",
      "3264       3      10.5    3081.0       3.0       1.0     748.0  -37.74160   \n",
      "9845       4       6.7    3058.0       4.0       2.0     441.0  -37.73572   \n",
      "10799      3      12.0    3073.0       3.0       1.0     606.0  -37.72057   \n",
      "2732       4       6.4    3011.0       4.0       2.0     319.0  -37.79430   \n",
      "\n",
      "       Longtitude  Propertycount    0  ...    6    7    8    9   10   11   12  \\\n",
      "12167   144.98670        13240.0  0.0  ...  0.0  0.0  0.0  0.0  0.0  0.0  0.0   \n",
      "6524    144.90050         6380.0  1.0  ...  0.0  0.0  0.0  0.0  0.0  0.0  0.0   \n",
      "8413    144.82200         3755.0  1.0  ...  0.0  0.0  0.0  0.0  0.0  0.0  0.0   \n",
      "2919    144.91580         8870.0  0.0  ...  1.0  0.0  0.0  0.0  1.0  0.0  0.0   \n",
      "6043    144.82720         4217.0  1.0  ...  0.0  0.0  0.0  0.0  0.0  0.0  0.0   \n",
      "...           ...            ...  ...  ...  ...  ...  ...  ...  ...  ...  ...   \n",
      "13123   144.95785        11918.0  1.0  ...  1.0  0.0  0.0  0.0  1.0  0.0  0.0   \n",
      "3264    145.04810         2947.0  1.0  ...  0.0  0.0  1.0  0.0  0.0  0.0  0.0   \n",
      "9845    144.97256        11204.0  1.0  ...  0.0  0.0  0.0  0.0  1.0  0.0  0.0   \n",
      "10799   145.02615        21650.0  1.0  ...  0.0  0.0  0.0  0.0  1.0  0.0  0.0   \n",
      "2732    144.88750         7570.0  1.0  ...  1.0  0.0  0.0  0.0  0.0  0.0  0.0   \n",
      "\n",
      "        13   14   15  \n",
      "12167  1.0  0.0  0.0  \n",
      "6524   0.0  1.0  0.0  \n",
      "8413   0.0  1.0  0.0  \n",
      "2919   0.0  0.0  0.0  \n",
      "6043   0.0  1.0  0.0  \n",
      "...    ...  ...  ...  \n",
      "13123  0.0  0.0  0.0  \n",
      "3264   0.0  0.0  0.0  \n",
      "9845   0.0  0.0  0.0  \n",
      "10799  0.0  0.0  0.0  \n",
      "2732   0.0  1.0  0.0  \n",
      "\n",
      "[10864 rows x 25 columns]\n",
      "MAE from Approach 3 (One-Hot Encoding):\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "166500.44717161093"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "'''\n",
    "方法3.One-Hot编码\n",
    "我们使用scikit-learn的OneHotEncoder类来获得one-hot编码。有许多参数可定义。\n",
    "设置handle_unknown='ignore'，以避免在验证数据包含训练数据中没有包括的值时发生错误\n",
    "设置sparse=False可以确保将已编码的列作为numpy数组(而不是稀疏矩阵)返回。\n",
    "'''\n",
    "from sklearn.preprocessing import OneHotEncoder\n",
    "\n",
    "# 将one-hot编码器分别应用于每一列分类变量\n",
    "OH_encoder = OneHotEncoder(handle_unknown='ignore', sparse=False)\n",
    "OH_cols_train = pd.DataFrame(OH_encoder.fit_transform(X_train[object_cols]))\n",
    "OH_cols_valid = pd.DataFrame(OH_encoder.transform(X_valid[object_cols]))\n",
    "\n",
    "# One-hot编码时移除了index;补回来\n",
    "OH_cols_train.index = X_train.index\n",
    "OH_cols_valid.index = X_valid.index\n",
    "\n",
    "# 删除分类列(将替换为One-hot编码),留下编码列\n",
    "num_X_train = X_train.drop(object_cols, axis=1)\n",
    "num_X_valid = X_valid.drop(object_cols, axis=1)\n",
    "\n",
    "# 向数值特征添加One-hot编码列\n",
    "OH_X_train = pd.concat([num_X_train, OH_cols_train], axis=1)\n",
    "OH_X_valid = pd.concat([num_X_valid, OH_cols_valid], axis=1)\n",
    "print(OH_X_train)\n",
    "print(\"MAE from Approach 3 (One-Hot Encoding):\")\n",
    "score_dataset(OH_X_train, OH_X_valid, y_train, y_valid)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0540b4bd",
   "metadata": {},
   "source": [
    "### 总结：\n",
    "删除分类变量(方法1)的性能最差，因为它的MAE分数最高。至于另外两种方法，由于返回的MAE分数值非常接近，没有太大差异。\n",
    "通常，one-hot编码(方法3)的效果最好，删除分类变量(方法1)的效果最差，但还得视情况而定。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
